module("framework.mvc.view.ViewMgr", package.seeall)

---@class ViewMgr : MiddleClass
---@field instance ViewMgr
local ViewMgr = class("ViewMgr")

function ViewMgr:ctor()
    self._viewSettings = nil
    self._viewContainerDict = {}
    self._openViewNameList = {}
    self._openViewNameSet = {}
    self._uiCanvas = nil
    self._uiRoot = nil
    self._topUICanvas = nil
    self._topUIRoot = nil
    self._uiLayerDict = {}
    LuaEventSystem.addEventMechanism(self)
end

function ViewMgr:init(viewSettings)
    self._viewSettings = viewSettings
    --通过ViewMgr派发事件，事件在ViewEvent中定义
    --ViewModalMaskMgr.instance:init()
    --ViewFullScreenMgr.instance:init()
    --ViewDestroyMgr.instance:init()
    -- ViewBackStackMgr.instance:init()
end

-- 打开一个带Tab界面并设置默认选中的Tab索引
function ViewMgr:openTabView(viewName, param, isImmediate, tabId1, tabId2, tabId3)
    param = param or {}
    param.defaultTabIds = {tabId1, tabId2, tabId3}
    self:openView(viewName, param, isImmediate)
end

-- 打开一个界面
function ViewMgr:openView(viewName, param, isImmediate)
    if viewName == nil or viewName == "" then
        logError("viewName is empty")
        return
    end
    local existViewContainer = self:getContainer(viewName)
    if self._openViewNameSet[viewName] and existViewContainer then
        --移除后再添加到列表最后
        tabletool.removeValue(self._openViewNameList, viewName)
        table.insert(self._openViewNameList, viewName)

        gohelper.setAsLastSibling(existViewContainer.viewGO)
        existViewContainer:onUpdateParamInternal(param)
        ViewMgr.instance:dispatchEvent(ViewEvent.ReOpenWhileOpen, viewName, param)
        return
    end

    local viewContainer = self:_createContainer(viewName)
    self._viewContainerDict[viewName] = viewContainer
    table.insert(self._openViewNameList, viewName)
    self._openViewNameSet[viewName] = true
    viewContainer:openInternal(param, isImmediate)
end

-- 关闭一个界面
function ViewMgr:closeView(viewName, isImmediate,closeManually)
    if not viewName or not self._openViewNameSet[viewName] then
        return
    end
    self._openViewNameSet[viewName] = nil
    tabletool.removeValue(self._openViewNameList, viewName)
    local viewContainer = self:getContainer(viewName)
    viewContainer:setCloseType(closeManually and BaseViewContainer.CloseTypeManual or nil)
    viewContainer:closeInternal(isImmediate)
end

-- 显式销毁一个界面，外部慎用
function ViewMgr:destroyView(viewName)
    local viewContainer = self:getContainer(viewName)
    if viewContainer then
        if viewContainer:isOpen() then
            viewContainer:closeInternal(true)
        end
        viewContainer:destroyView()
        viewContainer:__onDispose()
        self._viewContainerDict[viewName] = nil

        if self:isFull(viewName) then
            ViewMgr.instance:dispatchEvent(ViewEvent.DestroyFullViewFinish, viewName)
        elseif self:isModal(viewName) then
            ViewMgr.instance:dispatchEvent(ViewEvent.DestroyModalViewFinish, viewName)
        end
        ViewMgr.instance:dispatchEvent(ViewEvent.DestroyViewFinish, viewName)
    end
end

-- 判断一个界面是否处于打开状态，或者正在打开（资源加载中，或者正在播放打开动画）
function ViewMgr:isOpen(viewName)
    local viewContainer = self:getContainer(viewName)
    return viewContainer and viewContainer:isOpen()
end

-- 判断一个界面是否正在打开（加载中，或者正在播打开动画）
function ViewMgr:isOpening(viewName)
    local viewContainer = self:getContainer(viewName)
    return viewContainer and viewContainer:isOpening()
end

-- 判断一个界面是否已经打开完成
function ViewMgr:isOpenFinish(viewName)
    local viewContainer = self:getContainer(viewName)
    return viewContainer and viewContainer:isOpenFinish()
end

-- 关闭所有已打开的模态框 module_views.lua中配置viewType = ViewType.Modal
-- @excludeArr 可选择不关闭的界面 {viewName,viewName} 默认nil
function ViewMgr:closeAllModalViews(excludeArr)
    local views
    --倒序关闭多个界面（后打开的先关闭）
    for i = #self._openViewNameList, 1, -1 do
        local viewName = self._openViewNameList[i]
        if not excludeArr or not tabletool.indexOf(excludeArr, viewName) then
            if self:isModal(viewName) then
                views = views or {}
                table.insert(views, viewName)
            end
        end
    end
    if views then
        for i = 1, #views do
            self:closeView(views[i], true)
        end
    end
end

-- 关闭所有已打开的业务逻辑界面 module_views.lua中配置layer = "POPUP" / "POPUP_TOP"
-- @excludeArr 可选择不关闭的界面 {viewName,viewName} 默认nil
function ViewMgr:closeAllPopupViews(excludeArr,closeManually)
    local views
    --倒序关闭多个界面（后打开的先关闭）
    for i = #self._openViewNameList, 1, -1 do
        local viewName = self._openViewNameList[i]
        local setting = self:getSetting(viewName)
        if not excludeArr or not tabletool.indexOf(excludeArr, viewName) then
            if setting.layer == UILayerName.PopUpTop or setting.layer == UILayerName.PopUp then
                views = views or {}
                table.insert(views, viewName)
            end
        end
    end
    if views then
        local len = #views
        for i = 1,len  do
            self:closeView(views[i], true,i == len and closeManually)
        end
    end
end

-- 关闭所有已打开界面
-- @excludeArr 可选择不关闭的界面 {viewName,viewName} 默认nil
function ViewMgr:closeAllViews(excludeArr)
    local views
    --倒序关闭多个界面（后打开的先关闭）
    for i = #self._openViewNameList, 1, -1 do
        local viewName = self._openViewNameList[i]
        if not excludeArr or not tabletool.indexOf(excludeArr, viewName) then
            views = views or {}
            table.insert(views, viewName)
        end
    end
    if views then
        for i = 1, #views do
            self:closeView(views[i], true)
        end
    end
end

--是否有hud以上层级的界面打开
function ViewMgr:IsPopUpViewOpen()
    for i = #self._openViewNameList, 1, -1 do
        local viewName = self._openViewNameList[i]
        local setting = self:getSetting(viewName)
        if setting.layer == UILayerName.PopUpTop or setting.layer == UILayerName.PopUp or setting.layer == UILayerName.Guide then
            return true
        end
    end
    return false
end

function ViewMgr:getSetting(viewName)
    return self._viewSettings[viewName]
end

function ViewMgr:getOpenViewNameList()
    return self._openViewNameList
end

function ViewMgr:hasOpenFullView()
    for _, viewName in ipairs(self._openViewNameList) do
        if self:isFull(viewName) then
            return true
        end
    end
    return false
end

function ViewMgr:isNormal(viewName)
    local viewSetting = self._viewSettings[viewName]
    return viewSetting and viewSetting.viewType == ViewType.Normal
end

function ViewMgr:isModal(viewName)
    local viewSetting = self._viewSettings[viewName]
    return viewSetting and viewSetting.viewType == ViewType.Modal
end

function ViewMgr:isFull(viewName)
    local viewSetting = self._viewSettings[viewName]
    return viewSetting and viewSetting.viewType == ViewType.Full
end

function ViewMgr:getUIRoot()
    if not self._uiRoot then
        self._uiRoot = gohelper.find("UIRoot")
    end
    return self._uiRoot
end

function ViewMgr:getTopUIRoot()
    if not self._topUIRoot then
        self._topUIRoot = gohelper.find("UIRoot")
    end
    return self._topUIRoot
end

function ViewMgr:getUICanvas()
    if not self._uiCanvas then
        self._uiCanvas = self:getUIRoot():GetComponent("Canvas")
    end
    return self._uiCanvas
end

function ViewMgr:getTopUICanvas()
    if not self._topUICanvas then
        self._topUICanvas = self:getTopUIRoot():GetComponent("Canvas")
    end
    return self._topUICanvas
end

function ViewMgr:getUILayer(uiLayerName)
    if not self.viewRoot then
        self.viewRoot = gohelper.find("Canvas/ViewRoot")
    end
    return self.viewRoot
end

function ViewMgr:getContainer(viewName)
    return self._viewContainerDict[viewName]
end

function ViewMgr:_createContainer(viewName)
    local viewContainer = self._viewContainerDict[viewName]
    if not viewContainer then
        local viewSetting = self:getSetting(viewName)
        if viewSetting == nil then
            logError("view setting is nil " .. viewName)
        end
        local classPath = getModulePath(viewSetting.container)
        if classPath then
            local classDefine = addGlobalModule(classPath)
            viewContainer = classDefine.New()
            viewContainer:__onInit()
            viewContainer:setSetting(viewName, viewSetting)
        else
            logError("ViewContainer class path not exist: " .. viewName)
        end
    end
    return viewContainer
end

ViewMgr.instance = ViewMgr.New()
return ViewMgr
